package org.eclipse.iota.bounds;

import static org.eclipse.iota.bounds.BoundType.LOWER;
import static org.eclipse.iota.bounds.BoundType.UPPER;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.iota.assignments.Assignment;
import org.eclipse.iota.evaluator.IotaConstantEvaluator;
import org.eclipse.iota.evaluator.IotaEvaluator.EvaluationException;
import org.eclipse.iota.model.Addition;
import org.eclipse.iota.model.Clause;
import org.eclipse.iota.model.Constant;
import org.eclipse.iota.model.Exponentiation;
import org.eclipse.iota.model.Expression;
import org.eclipse.iota.model.Multiplication;
import org.eclipse.iota.model.Negation;
import org.eclipse.iota.util.IotaVariableUtil;

/**
 * @author Christian Krause
 * @generated NOT
 * @deprecated Use IotaRangeCalculator
 */
public class OldIotaBoundCalculator {

	/**
	 * Calculate the lower bound for a expression in a clause. Returns <code>null</code>
	 * if now lower bound was found. A lower bound is a constant smaller or equal than
	 * all possible values of the argument variable.
	 */
	public static Constant calculateLowerBound(Expression expression, Clause clause) {
		return calculateBound(expression, clause, LOWER);
	}
	
	/**
	 * Calculate the upper bound for an expression in a clause. Returns <code>null</code>
	 * if now upper bound was found. An upper bound is a constant larger or equal then
	 * all possible values of the argument variable.
	 */
	public static Constant calculateUpperBound(Expression expression, Clause clause) {
		return calculateBound(expression, clause, UPPER);
	}

	/**
	 * Calculate a bound for an expression.
	 */
	public static Constant calculateBound(Expression expression, Clause clause, BoundType type) {
		return calculateBound(expression, clause, type, new HashSet<Expression>());		
	}
	
	
	/*
	 * Calculate a bound for an expression.
	 */
	static Constant calculateBound(Expression expression, Clause clause, 
									BoundType boundType, Set<Expression> visited) {
		
		// Already visited?
		if (visited.contains(expression)) return null;

		Constant found = null, calculated = null;

		// If it is not a constant...
		if (!(expression instanceof Constant)) {
			
			// Mark the expression as visited
			visited.add(expression);
			
			// Try to find the bound.
			found = OldIotaBoundFinder.findBound(expression, clause, boundType, visited);
			if (found!=null) found = found.copy();			
		}
		
		
		
		// ----- CALCULATE THE BOUND ------ //

		
		// Constant?
		if (expression instanceof Constant) {
			calculated = ((Constant) expression).copy();			
		}
		
		// Expression without variables?
		else if (IotaVariableUtil.getUsedVariables(expression).isEmpty()) {
			IotaConstantEvaluator evaluator = new IotaConstantEvaluator(new Assignment());
			try {
				calculated = evaluator.evaluate(expression);
			} catch (EvaluationException e) {
				calculated = null;
			}
		}
		
		// Addition?
		else if (expression instanceof Addition) {
			
			Addition addition = (Addition) expression;
			calculated = new Constant(0);
			
			for (Expression operand : addition.getOperands()) {
				Constant value = calculateBound(operand, clause, boundType, visited);
				if (value==null) {
					calculated = null;
					break;
				} else {
					calculated.add(value);					
				}				
			}
			
		}
		
		// Multiplication?
		else if (expression instanceof Multiplication) {
			
			Multiplication multiplication = (Multiplication) expression;
			calculated = new Constant(1);
			
			for (Expression operand : multiplication.getOperands()) {
				
				// Compute lower and upper bound.
				Constant lower = calculateBound(operand, clause, LOWER, visited);
				Constant upper = calculateBound(operand, clause, UPPER, visited);
				
				// Make sure they have the correct sign.
				if (lower!=null && lower.isNegative()) lower = null;
				if (upper!=null && upper.isPositive()) upper = null;
				
				if (boundType==LOWER) {
					if (lower!=null) calculated.multiply(lower);
					else if (upper!=null) calculated.multiply(upper);
					else calculated=null;
				} else {
					if (upper!=null) calculated.multiply(upper);
					else if (lower!=null) calculated.multiply(lower);
					else calculated=null;				
				}
				
				// Stop already?
				if (calculated==null) break;
			}
			
			if (calculated!=null) {
				if (boundType==LOWER && calculated.isNegative()) calculated = null; else
				if (boundType==UPPER && calculated.isPositive()) calculated = null;
			}
			
		}

		// Negation?
		else if (expression instanceof Negation) {
			
			// We have to flip the bound type!
			Negation negation = (Negation) expression;			
			calculated = calculateBound(negation.getOperand(), clause, boundType.flip(), visited);
			
		}
		
		// Exponentiation? Only for lower bounds!
		else if (expression instanceof Exponentiation && boundType==LOWER) {
			
			Exponentiation exponentiation = (Exponentiation) expression;
			Constant base = calculateBound(exponentiation.getLeft(), clause, boundType, visited);
			Constant exponent = calculateBound(exponentiation.getRight(), clause, boundType, visited);
			
			if (base!=null && exponent!=null) {
				// We can calculate it only if the base is positive and the exponent is not negative!
				if (base.isPositive() && !exponent.isNegative() && exponent.isShort()) {
					calculated = base.pow(exponent.getValue().intValue());
				}
			}
			
		}
				
		// Decide which bound is better.
		if (found!=null && calculated!=null) {
			return chooseBetter(found, calculated, boundType);
		}
		
		// Otherwise take the one that is not null.
		if (calculated!=null) return calculated;
		return found;
		
	}
	
	
	public static Constant chooseBetter(Constant b1, Constant b2, BoundType type) {
		if (type==LOWER) {
			return b1.isGreaterThan(b2) ? b1 : b2;
		}
		if (type==UPPER) {
			return b1.isLessThan(b2) ? b1 : b2;
		}
		return null;
	}

}
